// JSON数组文件操作
import path from 'path';
import fs from 'fs-extra';
const _ = require('lodash');

export default class Json {
    constructor(file, id, name) {
        this.file = file;
        this.data = null;
        this.id = id || 'id';
        this.name = name || '';
        this.lastModified = null;
        if (typeof this.init === 'function') {
            this.init();
        }
    }

    load() {
        return new Promise((done, fail) => {
            if (!this.no_file_change) {
                // 获取文件修改时间
                fs.stat(this.file, (err, stats) => {
                    if (err) {
                        fail(err);
                    } else {
                        if (this.lastModified !== stats.mtime) {
                            // 有更新则读取
                            this.lastModified = stats.mtime;
                            done(true);
                        } else {
                            // 否则不读
                            done(false);
                        }
                    }
                });
            } else done(false);
        }).then(reload => new Promise((done, fail) => {
            if (reload || this.data == null) {
                fs.readJson(this.file, (err, data) => {
                    if (err) {
                        fail(err);
                    } else {
                        this.data = data;
                        if (typeof this.reload === 'function') {
                            // 重新初始化
                            this.reload(data);
                        }
                        done(data);
                    }
                });
            } else done(this.data);
        })).catch(err => {
            if (err.code === 'ENOENT') {
                // 文件不存在则返回空列表
                this.lastModified = null;
                return this.data = [];
            }
            throw err;
        });
    }

    save(result) {
        return new Promise((done, fail) => {
            fs.ensureDir(path.dirname(this.file), err => {
                if (err) {
                    fail(err);
                } else {
                    done(result);
                }
            });
        }).then(result => new Promise((done, fail) => {
            fs.writeJSON(this.file, this.data, err => {
                if (err) {
                    fail(err);
                } else {
                    done(result);
                }
            });
        }));
    }

    q(id) {
        const q = {};
        q[this.id] = id;
        return q;
    }

    _find(data, id) {
        return _.find(data, this.q(id));
    }

    get(id) {
        return this.load().then(data => {
            const item = this._find(data, id);
            if (!item) {
                throw `${this.name}${id}不存在`;
            }
            return item;
        });
    }

    _400(message) {
        return Promise.reject(message);
    }

    _409(id, data) {
        let text;
        if (typeof this.tip409 === 'function') {
            text = this.tip409(id, data);
        } else text = `${this.name}${id}已存在`;
        throw text;
    }

    create(json) {
        const item = this.check(json, true);
        if (typeof item === 'string') {
            return this._400(item);
        }
        return this.load().then(data => {
            const id = item[this.id];
            const old = this._find(data, id);
            if (this.allow_create_exists) {
                if (old) {
                    _.extend(old, item);
                } else data.push(item);
            } else {
                if (old) {
                    this._409(id, item);
                }
                data.push(item);
            }
            return item;
        }).then(this.save.bind(this));
    }

    modify(id, json) {
        const item = this.check(json);
        if (typeof item === 'string') {
            return this._400(item);
        }
        return this.load().then(data => {
            const old = this._find(data, id);
            if (!old) {
                throw 404;
            }
            const id2 = item[this.id];
            if (id !== id2 && this._find(data, id2)) {
                this._409(id2, item);
            }
            _.extend(old, item);
        }).then(this.save.bind(this));
    }

    remove(id) {
        return this.load().then(data => {
            if (id) {
                _.remove(data, this.q(id));
            } else {
                this.data = [];
            }
        }).then(this.save.bind(this));
    }
}
